;**** A P P L I C A T I O N   N O T E   A V R 2 4 0 ************************
;* 
;* Title:		4x4 keypad, wake-up on keypress
;* Version:		1.1
;* Last Updated:	98.10.22
;* Target:		All AVR Devices 
;*
;* Support E-mail:	avr@atmel.com
;*
;* DESCRIPTION
;* This Application note scans a 4 x 4 keypad and uses sleep mode
;* causing the AVR to wake up on keypress.  The design uses a minimum of
;* external components. Included is a test program that wakes up the AVR
;* and performs a scan when a key is pressed and flashes one of two LEDs 
;* the number of the key pressed.  The external interrupt line is used for
;* wake-up.  The example runs on the AT90S1200 but can be any AVR with suitable
;* changes in vectors, EEPROM and stack pointer. The timing assumes a 4 MHz clock.
;* A look up table is used in EEPROM to enable the same structure to be used
;* with more advanced programs e.g ASCII output to displays.
;***************************************************************************

;***** Register used by all programs
;******Global variable used by all routines

.def	temp	=r16	;general scratch space

;Port B pins

.equ	ROW1	=3	;keypad input rows
.equ	ROW2	=2
.equ	ROW3	=1
.equ	ROW4	=0
.equ	COL1	=7	;keypad output columns
.equ	COL2	=6
.equ	COL3	=5	
.equ	COL4	=4	

;Port D pins

.equ	GREEN	=0	;green LED
.equ	RED	=1	;red LED
.equ	INT	=2	;interrupt input

.include "1200def.inc"

;***** Registers used by interrupt service routine


.def	key	=r17	;key pointer for EEPROM
.def	status	=r21	;preserve sreg here

;***** Registers used by delay subroutine
;***** as local variables

.def	fine	=r18	;loop delay counters
.def	medium	=r19	
.def 	coarse	=r20

;*****Look up table for key conversion******************************
.eseg					;EEPROM segment
.org 0

	.db	1,2,3,15,4,5,6,14,7,8,9,13,10,0,11,12
;****Source code***************************************************
.cseg					;CODE segment
.org 0
		rjmp reset		;Reset handler
		rjmp scan		;interrupt service routine	
		reti			;unused timer interrupt
		reti			;unused analogue interrupt

;*** Reset handler **************************************************
reset:		
		
		ldi temp,0xFB		;initialise port D as O/I
		out DDRD,temp		;all OUT except PD2 ext.int.
		ldi temp,0x30		;turn on sleep mode and power
		out MCUCR,temp		;down plus interrupt on low level.
		ldi temp,0x40		;enable external interrupts
		out GIMSK,temp
		sbi ACSR,ACD		;shut down comparator to save power
main:		cli			;disable global interrupts 
		ldi temp,0xF0		;initialise port B as I/O
		out DDRB,temp		; 4 OUT  4 IN
		ldi temp,0x0F		;key columns all low and
		out PORTB,temp		;active pull ups on rows enabled
		ldi temp,0x07		;enable pull up on PD2 and
		out PORTD,temp		;turn off LEDs
		sei			;enable global interrupts ready
		sleep			;fall asleep
		rcall flash		;flash LEDs for example usage
		ldi temp,0x40
		out GIMSK,temp		;enable external interrupt
		rjmp main		;go back to sleep after keyscan

;****Interrupt service routine***************************************
scan:		
		in status,SREG		;preserve status register
		sbis PINB,ROW1		;find row of keypress
		ldi key,0		;and set ROW pointer
		sbis PINB,ROW2
		ldi key,4
		sbis PINB,ROW3
		ldi key,8
		sbis PINB,ROW4
		ldi key,12
		ldi temp,0x0F		;change port B I/O to
		out DDRB,temp		;find column press
		ldi temp,0xF0		;enable pull ups and
		out PORTB,temp		;write 0s to rows
		rcall settle		;allow time for port to settle
		sbis PINB,COL1		;find column of keypress
		ldi temp,0		;and set COL pointer
		sbis PINB,COL2
		ldi temp,1
		sbis PINB,COL3
		ldi temp,2
		sbis PINB,COL4
		ldi temp,3
		add key,temp		;merge ROW and COL for pointer
		ldi temp,0xF0		;reinitialise port B as I/O
		out DDRB,temp		; 4 OUT  4 IN
		ldi temp,0x0F		;key columns all low and
		out PORTB,temp		;active pull ups on rows enabled
		out SREG,status		;restore status register

		ldi temp,0x00
		out GIMSK,temp 		;disable external interrupt
					;have to do this, because we're
					;using a level-triggered interrupt

		reti			;go back to main for example program

;***Example test program to flash LEDs using key press data************

flash:		out EEAR,key		;address EEPROM
		sbi EECR,EERE		;strobe EEPROM
		in temp,EEDR		;set number of flashes
		tst temp		;is it zero?
		breq zero		;do RED LED
green_flash:
			cbi PORTD,GREEN	;flash green LED 'temp' times
			rcall delay
			sbi PORTD,GREEN
			rcall delay
			dec temp
			brne green_flash
exit:		ret
zero:		ldi temp,10
flash_again:		cbi PORTD,RED	;flash red LED ten times
			rcall delay
			sbi PORTD,RED
			rcall delay
			dec temp
			brne flash_again
		rjmp exit

		
;****Time Delay Subroutine for LED flash*********************************
delay:
	ldi coarse,8			;triple nested FOR loop
cagain:		ldi medium,255		;giving about 1/2 second
magain:			ldi fine,255	;delay on 4 MHz clock
fagain:			dec fine
			brne fagain
		dec medium
		brne magain
	dec coarse
	brne cagain
	ret

;***Settling time delay for port to stabilise******************************	
settle:
	ldi temp,255
tagain:		dec temp
		brne tagain
	ret